package database;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import ukoly.*;
import uzivatele.*;

/** Trida pro praci s databazi.  
 *
 * @author tomas.marvan
 */
public class DbService {

    public DbService() {
    }

    /**Vybere z databaze uzivatele podle zadane podminky.
     * 
     * @param podminka cast dotazu WHERE. Napr. "where name like'Ka%'". Pro vyber vsech uzivatelu bez omezeni pouzij "" - prazdny retezec.
     * @return Seznam uzivatelu vyhovujici danne podmince.
     * @throws DatabazovaChyba 
     */
    public ArrayList<Uzivatel> vyberUzivatele(String podminka) throws DatabazovaChyba {
        ArrayList<Uzivatel> ret = new ArrayList<Uzivatel>();
        ArrayList<HashMap<String, String>> tmp = this.provedSelect("SELECT * FROM users " + podminka);
        for (HashMap<String, String> hm : tmp) {
            try {
                ret.add(this.parseToUzivatelObject(hm));
            } catch (NeprevoditelnyObjekt ex) {
                ex.printStackTrace();
            }
        }

        return ret;
    }

    /**Ulozi jiz existujiciho uzivatele. V databazi se tento uzivatel pozna podle id.
     * 
     * @param uzivatel Uzivatel ktery ma byt editovan.
     * @throws DatabazovaChyba 
     */
    public void ulozUzivatele(Uzivatel uzivatel) throws DatabazovaChyba {
        String pom;
        pom = uzivatel.getHeslo() == null ? "" : "', password='" + vratMd5(uzivatel.getHeslo());
        String description = (uzivatel.getPopis() == null) ? "NULL" : "'" + uzivatel.getPopis() + "'";
        this.provedUpdate("UPDATE users SET name='" + uzivatel.getJmeno() + "', surname='" + uzivatel.getPrijmeni()
                + "', description=" + description + ", rank='" + uzivatel.getRank()
                + "', email='" + uzivatel.getEmail() + pom
                + "' WHERE user_id='" + uzivatel.getId() + "'");
    }

    /**Vytvori v databazi noveho uzivatele a vrati objek Uzivatel s platnym id.
     * 
     * @param uzivatel uzivatel s libovolnou hodnotou id. Id pridelene z databaze bude vraceno v navratovem objektu Uzivatel.
     * @return uzivatel vytvoreny v databazi. Oproti vstupnimu paramentru obsahuje platne id.
     * @throws DatabazovaChyba 
     */
    public Uzivatel vytvorUzivatele(Uzivatel uzivatel) throws DatabazovaChyba {
        String description = (uzivatel.getPopis() == null) ? "NULL" : "'" + uzivatel.getPopis() + "'";
        this.provedUpdate("INSERT INTO users (name, surname, description, rank, email, password) VALUES "
                + "('" + uzivatel.getJmeno() + "', '" + uzivatel.getPrijmeni() + "', " + description
                + ", '" + uzivatel.getRank() + "', '" + uzivatel.getEmail() + "', '" + vratMd5(uzivatel.getHeslo()) + "')");
        ArrayList<HashMap<String, String>> newUserId = this.provedSelect("SELECT user_id FROM users WHERE email='" + uzivatel.getEmail() + "'");
        int id = Integer.parseInt(newUserId.get(0).get("user_id"));
        Uzivatel ret = new Uzivatel(id, uzivatel.getJmeno(), uzivatel.getPrijmeni(), uzivatel.getEmail(), uzivatel.getPopis(), uzivatel.getRank(), uzivatel.getHeslo());
        return ret;
    }

    /**Smaze v databazi daneho uzivatele. Pozna se podle id.
     * 
     * @param uzivatel uzivatel, ketry bude smazan.
     * @throws DatabazovaChyba 
     */
    public void smazUzivatele(Uzivatel uzivatel) throws DatabazovaChyba {
        provedUpdate("DELETE FROM users WHERE user_id='" + uzivatel.getId() + "'");
    }

    /**Ulozi jiz existujici Komentar. V databazi se tento Komentar pozna podle id.
     * 
     * @param Komentar Komentar ktery ma byt editovan.
     * @throws DatabazovaChyba 
     */
    public void ulozKomentar(Komentar komentar) throws DatabazovaChyba {
        this.provedUpdate("UPDATE comments SET text='" + komentar.getCommentText()
                + "' WHERE comment_id='" + komentar.getID() + "'");
    }

    /**Vytvori v databazi novy Komentar a vrati objek Komentar s platnym id.
     * 
     * @param komentar Komentar s libovolnou hodnotou id. Id pridelene z databaze bude vraceno v navratovem objektu Komentar.
     * @return Komentar vytvoreny v databazi. Oproti vstupnimu paramentru obsahuje platne id.
     * @throws DatabazovaChyba 
     */
    public Komentar vytvorKomentar(Komentar komentar) throws DatabazovaChyba {
        this.provedUpdate("INSERT INTO comments (task_id, text, user_id, date_created) VALUES "
                + "('" + komentar.getUkol().getModel().getID() + "', '" + komentar.getCommentText()
                + "', '" + komentar.getAuthor().getId() + "', '" + this.calendarToSQLDate(komentar.getTimeCreation(), true) + "')");
        ArrayList<HashMap<String, String>> newCommentId = this.provedSelect("SELECT comment_id FROM comments WHERE task_id='"
                + komentar.getUkol().getModel().getID() + "' AND user_id='" + komentar.getAuthor().getId()
                + "' AND date_created='" + this.calendarToSQLDate(komentar.getTimeCreation(), true) + "' ORDER BY comment_id DESC");
        int id = Integer.parseInt(newCommentId.get(0).get("comment_id"));
        Komentar ret = new Komentar(komentar.getCommentText(), komentar.getAuthor(), id, komentar.getTimeCreation(), komentar.getUkol());
        return ret;
    }

    /**Smaze v databazi danny komentar. Ten se pozna podle id.
     * 
     * @param komentar komentar, ktery ma byt smazan
     * @throws DatabazovaChyba 
     */
    public void smazKomentar(Komentar komentar) throws DatabazovaChyba {
        provedUpdate("DELETE FROM comments WHERE comment_id='" + komentar.getID() + "'");
    }

    /**Vybere z databaze Ukoly podle zadane podminky. Vrácené objekty obsahuji jiz naplnene seznamy komentaru.
     * 
     * @param podminka cast dotazu WHERE. Napr. "where user_id='3'". Pro vyber vsech Ukolu bez omezeni pouzij "" - prazdny retezec.
     * @return Seznam ukolu vyhovujici danne podmince.
     * @throws DatabazovaChyba 
     */
    public ArrayList<UkolControl> vyberUkoly(String podminka) throws DatabazovaChyba {
        ArrayList<UkolControl> ret = new ArrayList<UkolControl>();
        ArrayList<HashMap<String, String>> tmp = this.provedSelect("SELECT * FROM tasks " + podminka);
        for (HashMap<String, String> hm : tmp) {
            try {
                ret.add(this.parseToUkolObject(hm));
            } catch (NeprevoditelnyObjekt ex) {
                ex.printStackTrace();
            }
        }

        return ret;
    }

    /**Ulozi jiz existujici ukol. V databazi se tento ukol pozna podle id. NEBUDOU ULOZENY ZMENENE KOMENTARE!!! - duvod je casova slozitost. 
     * Pro ulozeni jednotlivych komentaru pouzijte metodu ulozKomentar(Komentar).
     * 
     * @param ukolControl Ukol ktery ma byt editovan.
     * @throws DatabazovaChyba 
     */
    public void ulozUkol(UkolControl ukolControl) throws DatabazovaChyba {
        String name = ukolControl.getModel().getJmeno();
        String description = (ukolControl.getModel().getPopis() == null) ? "NULL" : "'" + ukolControl.getModel().getPopis() + "'";
        String deadline = this.calendarToSQLDate(ukolControl.getModel().getCasDead(), false);
        String userId = (ukolControl.getModel().getVlastnik() == null) ? "NULL" : "'" + ukolControl.getModel().getVlastnik().getId() + "'";
        String status = ukolStatusToString(ukolControl.getModel().getStatus());
        int timeSpentInt = ukolControl.getModel().getCasStraven();
        String dateStart = calendarToSQLDate(ukolControl.getModel().getCasStart(), false);
        String dateEnd = (calendarToSQLDate(ukolControl.getModel().getCasKonec(), false) == null) ? "NULL" : "'" + calendarToSQLDate(ukolControl.getModel().getCasKonec(), false) + "'";
        int taskId = ukolControl.getModel().getID();

        this.provedUpdate("UPDATE tasks SET project_id='"
                + ukolControl.getModel().getProjekt().getModel().getID()
                + "', name='" + name + "', description=" + description
                + ", date_deadline='" + deadline + "', user_id=" + userId
                + ", status='" + status + "', time_spent='" + timeSpentInt
                + "', date_start='" + dateStart + "', date_end=" + dateEnd
                + " WHERE task_id='" + taskId + "'");
    }

    /**Vytvori v databazi novy ukol a vrati objek UkolControl s platnym id.
     * 
     * @param ukolControl Ukol s libovolnou hodnotou id. Id pridelene z databaze bude vraceno v navratovem objektu UkolControl.
     * @return UkolControl Ukol vytvoreny v databazi. Oproti vstupnimu paramentru obsahuje platne id.
     * @throws DatabazovaChyba 
     */
    public UkolControl vytvorUkol(UkolControl ukolControl) throws DatabazovaChyba {
        String description = (ukolControl.getModel().getPopis() == null) ? "NULL" : "'" + ukolControl.getModel().getPopis() + "'";
        String userId = (ukolControl.getModel().getVlastnik() == null || ukolControl.getModel().getVlastnik().getId() == 0) ? "NULL" : "'" + ukolControl.getModel().getVlastnik().getId() + "'";
        String dateEnd = (calendarToSQLDate(ukolControl.getModel().getCasKonec(), false) == null) ? "NULL" : "'" + calendarToSQLDate(ukolControl.getModel().getCasKonec(), false) + "'";
        this.provedUpdate("INSERT INTO tasks (project_id, name, description, date_deadline, user_id, status, time_spent, date_start, date_end) VALUES "
                + "('" + ukolControl.getModel().getProjekt().getModel().getID() + "', '" + ukolControl.getModel().getJmeno() + "', " + description
                + ", '" + this.calendarToSQLDate(ukolControl.getModel().getCasDead(), false) + "', " + userId
                + ", '" + ukolStatusToString(ukolControl.getModel().getStatus()) + "', '" + ukolControl.getModel().getCasStraven() + "', '" + this.calendarToSQLDate(ukolControl.getModel().getCasStart(), false)
                + "', " + dateEnd + ")");
        ArrayList<HashMap<String, String>> newTaskId = this.provedSelect("SELECT task_id FROM tasks WHERE project_id='"
                + ukolControl.getModel().getProjekt().getModel().getID() + "' AND name='" + ukolControl.getModel().getJmeno() + "'"
                + " ORDER BY task_id DESC");
        int id = Integer.parseInt(newTaskId.get(0).get("task_id"));
        UkolControl ret = new UkolControl(id, ukolControl.getModel().getJmeno(), ukolControl.getModel().getPopis(), ukolControl.getModel().getCasStart(), ukolControl.getModel().getCasKonec(), ukolControl.getModel().getCasDead(), ukolControl.getModel().getProjekt(), ukolControl.getModel().getVlastnik(), ukolControl.getModel().getStatus(), ukolControl.getModel().getKomentare(), ukolControl.getModel().getCasStraven());
        return ret;
    }

    /**Smaze v databazi danny ukol. ten se pozna podle id.
     * 
     * @param ukol ukol, ktery bude smazan
     * @throws DatabazovaChyba 
     */
    public void smazUkol(UkolControl ukol) throws DatabazovaChyba {
        provedUpdate("DELETE FROM tasks WHERE task_id='" + ukol.getModel().getID() + "'");
    }

    /**Vybere z databaze Projekty podle zadane podminky.
     * 
     * @param podminka cast dotazu WHERE. Napr. "where name='dulezity projekt'". Pro vyber vsech projektu bez omezeni pouzij "" - prazdny retezec.
     * @return Seznam Projektu vyhovujici danne podmince.
     * @throws DatabazovaChyba 
     */
    public ArrayList<ProjektControl> vyberProjekty(String podminka) throws DatabazovaChyba {
        ArrayList<ProjektControl> ret = new ArrayList<ProjektControl>();
        ArrayList<HashMap<String, String>> tmp = this.provedSelect("SELECT * FROM projects " + podminka);
        for (HashMap<String, String> hm : tmp) {
            try {
                ret.add(this.parseToProjectObject(hm));
            } catch (NeprevoditelnyObjekt ex) {
                ex.printStackTrace();
            }
        }

        return ret;
    }

    /**Ulozi jiz existujici projekt. V databazi se tento projekt pozna podle id.
     * 
     * @param projekt Projekt ktery ma byt editovan.
     * @throws DatabazovaChyba 
     */
    public void ulozProjekt(ProjektControl projekt) throws DatabazovaChyba {
        String description = (projekt.getModel().getPopis() == null) ? "NULL" : "'" + projekt.getModel().getPopis() + "'";
        String dateEnd = (calendarToSQLDate(projekt.getModel().getCasKonec(), false) == null) ? "NULL" : "'" + calendarToSQLDate(projekt.getModel().getCasKonec(), false) + "'";
        this.provedUpdate("UPDATE projects SET name='" + projekt.getModel().getJmeno() + "', description=" + description
                + ", date_start='" + calendarToSQLDate(projekt.getModel().getCasStart(), false) + "', date_end=" + dateEnd
                + ", date_deadline='" + calendarToSQLDate(projekt.getModel().getCasDead(), false) + "', time_spent='" + projekt.getModel().getCasStraven()
                + "', user_id='" + projekt.getModel().getOdpovedny().getId() + "', status='" + this.ukolStatusToString(projekt.getModel().getStatus()) + "' WHERE project_id='" + projekt.getModel().getID() + "'");
    }

    /**Vytvori v databazi novy projekt a vrati objek ProjektControl s platnym id.
     * 
     * @param projekt Projekt s libovolnou hodnotou id. Id pridelene z databaze bude vraceno v navratovem objektu Projekt.
     * @return ProjektControl Projek vytvoreny v databazi. Oproti vstupnimu paramentru obsahuje platne id.
     * @throws DatabazovaChyba 
     */
    public ProjektControl vytvorProjekt(ProjektControl projekt) throws DatabazovaChyba {
        String description = (projekt.getModel().getPopis() == null) ? "NULL" : "'" + projekt.getModel().getPopis() + "'";
        String dateEnd = (calendarToSQLDate(projekt.getModel().getCasKonec(), false) == null) ? "NULL" : "'" + calendarToSQLDate(projekt.getModel().getCasKonec(), false) + "'";
        this.provedUpdate("INSERT INTO projects (name, description, date_start, date_end, date_deadline, time_spent, user_id, status) VALUES "
                + "('" + projekt.getModel().getJmeno() + "', " + description + ", '" + this.calendarToSQLDate(projekt.getModel().getCasStart(), false)
                + "', " + dateEnd + ", '" + this.calendarToSQLDate(projekt.getModel().getCasDead(), false)
                + "', '" + projekt.getModel().getCasStraven() + "', '" + projekt.getModel().getOdpovedny().getId() + "', '" + ukolStatusToString(projekt.getModel().getStatus()) + "')");
        ArrayList<HashMap<String, String>> newProjectId = this.provedSelect("SELECT project_id FROM projects WHERE name='"
                + projekt.getModel().getJmeno() + "'"
                + " AND user_id='" + projekt.getModel().getOdpovedny().getId() + "' ORDER BY project_id DESC");
        int id = Integer.parseInt(newProjectId.get(0).get("project_id"));
        ProjektControl ret = new ProjektControl(id, projekt.getModel().getJmeno(), projekt.getModel().getPopis(), projekt.getModel().getCasStart(), projekt.getModel().getCasKonec(),
                projekt.getModel().getCasDead(), projekt.getModel().getCasStraven(), projekt.getModel().getOdpovedny(), projekt.getModel().getStatus());
        return ret;
    }

    /**Smaze v databazi danny projekt. Ten se pozna podle id.
     * 
     * @param projekt projekt, ketry bude smazan
     * @throws DatabazovaChyba 
     */
    public void smazProjekt(ProjektControl projekt) throws DatabazovaChyba {
        provedUpdate("DELETE FROM projects WHERE project_id='" + projekt.getModel().getID() + "'");
    }

    /**Metoda pro praci s daty v databazi bez navratove hodnoty.
     * 
     * @param sqlUpdate sql prikaz zacinajici UPDATE, DELETE, INSERT
     * @throws DatabazovaChyba pri chybe databaze kterou dokaze zachytit obsluzny php skript
     */
    private void provedUpdate(String sqlUpdate) throws DatabazovaChyba {
        String odpoved = "";
        try {
            FtpZmenaSouboru("ftp.spravceukolu.tym.cz", "spravceukolu.tym.cz", "spravceukoluA", "tmpFile.txt", sqlUpdate);

            URL url = new URL("http://www.spravceukolu.tym.cz/dbservice.php?passwd=" + vratMd5("spravceukoluA") + "&mode=update");
            odpoved = vratOdpovedServeru(url);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        if (odpoved.startsWith("ERROR:")) {
            throw new DatabazovaChyba(odpoved);
        }
    }

    /**Metoda pro praci s daty v databazi bez navratove hodnoty.
     * 
     * @param sqlSelect sql prikaz zacinajici SELECT
     * @return data vracena z databaze pri zavolani prikazu sqlSelect 
     * @throws DatabazovaChyba pri chybe databaze kterou dokaze zachytit obsluzny php skript
     */
    private ArrayList<HashMap<String, String>> provedSelect(String sqlSelect) throws DatabazovaChyba {
        String odpoved = "";
        try {
            FtpZmenaSouboru("ftp.spravceukolu.tym.cz", "spravceukolu.tym.cz", "spravceukoluA", "tmpFile.txt", sqlSelect);

            URL url = new URL("http://www.spravceukolu.tym.cz/dbservice.php?passwd=" + vratMd5("spravceukoluA") + "&mode=select");
            odpoved = vratOdpovedServeru(url);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        if (odpoved.startsWith("ERROR:")) {
            throw new DatabazovaChyba(odpoved);
        }

        ArrayList<HashMap<String, String>> ret = new ArrayList<HashMap<String, String>>();

        String[] lines = odpoved.split("<br>");
        HashMap<String, String> zaznam = null;
        String klic = null;
        String hodnota = null;
        char ch = ' ';
        for (String line : lines) {
            zaznam = new HashMap<String, String>();
            while (!line.isEmpty()) {
                klic = "";
                hodnota = "";
                ch = line.charAt(0);
                line = line.substring(1);
                while (ch != ':') {
                    klic += ch;
                    ch = line.charAt(0);
                    line = line.substring(1);
                }

                //oriznu uvodni uvozovky
                line = line.substring(1);
                ch = line.charAt(0);
                line = line.substring(1);
                while (ch != '\"') {
                    if (ch == '\\' && line.length() > 0 && line.charAt(0) == '\"') {
                        hodnota += "\"";
                        ch = line.charAt(1);
                        line = line.substring(2);
                        continue;
                    }

                    hodnota += ch;
                    ch = line.charAt(0);
                    line = line.substring(1);
                }

                zaznam.put(klic, hodnota);
            }
            if (!zaznam.isEmpty()) {
                ret.add(zaznam);
            }

        }

        return ret;
    }

    /** Vrati odpoved z weboveho serveru specifikovaneho url.
     * 
     * @param url url pro pripojeni k serveru
     * @return odpoved serveru
     * @throws IOException 
     */
    private String vratOdpovedServeru(URL url) throws IOException {
        StringBuilder ret = new StringBuilder();

        URLConnection conn = url.openConnection();
        BufferedReader in = new BufferedReader(
                new InputStreamReader(
                conn.getInputStream(), "UTF-8"));
        String inputLine;

        while ((inputLine = in.readLine()) != null) {
            if (!inputLine.startsWith("<")) {
                ret.append(inputLine);
            }
        }

        in.close();
        return ret.toString();
    }

    /**Vrati md5 hash z daneho retezce.
     * 
     * @param str retezec pro hashovaci funkci
     * @return hd5 hash
     * @throws NoSuchAlgorithmException 
     */
    private String vratMd5(String str) {
        try {
            String md5String = "";

            MessageDigest algorithm = MessageDigest.getInstance("MD5");
            algorithm.reset();
            algorithm.update(str.getBytes());
            byte[] md5 = algorithm.digest();
            String tmp = "";
            for (int i = 0; i < md5.length; i++) {
                tmp = (Integer.toHexString(0xFF & md5[i]));
                if (tmp.length() == 1) {
                    md5String += "0" + tmp;
                } else {
                    md5String += tmp;
                }
            }
            return md5String;
        } catch (NoSuchAlgorithmException ex) {
            ex.printStackTrace();
            return null;
        }
    }

    /**Zmeni (pripadne vytvori) soubor na danem ftp serveru. Jako obsah souboru bude pouzit obsah parametru text.
     * 
     * @param server ftp server (ftp.myserver.com)
     * @param uzivatel uzivatelske jmeno pro pripojeni
     * @param heslo heslo pro pripojeni
     * @param jmenoSouboru jmeno textoveho souboru na ftp serveru
     * @param text text ktery bude napsan do souboru souboru
     * @throws IOException 
     */
    private void FtpZmenaSouboru(String server, String uzivatel, String heslo, String jmenoSouboru, String text) throws IOException {
        SimpleFTP ftp = new SimpleFTP();
        ftp.connect(server, 21, uzivatel, heslo);
        ftp.bin();

        File file = new File(jmenoSouboru);
        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF8"));
        out.write(text);
        out.close();

        ftp.stor(file);
        ftp.disconnect();
        file.delete();
    }

    private Uzivatel parseToUzivatelObject(HashMap<String, String> hm) throws NeprevoditelnyObjekt {
        Uzivatel ret = null;
        if (hm.containsKey("user_id") && hm.containsKey("name") && hm.containsKey("surname") && hm.containsKey("email") && hm.containsKey("description") && hm.containsKey("rank") && hm.containsKey("password")) {
            ret = new Uzivatel(Integer.parseInt(hm.get("user_id")), hm.get("name"), hm.get("surname"), hm.get("email"), hm.get("description"), Integer.parseInt(hm.get("rank")), hm.get("password"));
        } else {
            throw new NeprevoditelnyObjekt("Objek nelze prevest na objekt typu Uzivatel.");
        }
        return ret;
    }

    private Komentar parseToKomentarObject(HashMap<String, String> hm, UkolControl vlastnik) throws NeprevoditelnyObjekt, DatabazovaChyba {
        Komentar ret = null;
        if (hm.containsKey("user_id") && hm.containsKey("task_id") && hm.containsKey("text") && hm.containsKey("comment_id") && hm.containsKey("date_created")) {

            ArrayList<HashMap<String, String>> uzivatelSelect = provedSelect("SELECT * FROM users WHERE user_id='" + hm.get("user_id") + "'");
            /*
             * Kontrola pro pripad, ze uzivatel je prirazen, ale uz byl vymazan z databaze, tj nejde dohledat
             */
            if (!uzivatelSelect.isEmpty()) {
                Uzivatel author = parseToUzivatelObject(uzivatelSelect.get(0));
                UkolControl ukol = (vlastnik == null) ? parseToUkolObject(this.provedSelect("SELECT * FROM tasks WHERE task_id='" + hm.get("task_id") + "'").get(0))
                        : vlastnik;
                ret = new Komentar(hm.get("text"), author, Integer.parseInt(hm.get("comment_id")), this.sqlDateToCalendar(hm.get("date_created")), ukol);
            } else {
                UkolControl ukol = (vlastnik == null) ? parseToUkolObject(this.provedSelect("SELECT * FROM tasks WHERE task_id='" + hm.get("task_id") + "'").get(0))
                        : vlastnik;
                ret = new Komentar(hm.get("text"), null, Integer.parseInt(hm.get("comment_id")), this.sqlDateToCalendar(hm.get("date_created")), ukol);
            }
        } else {
            throw new DatabazovaChyba("Objekt nelze prevest na objekt typu Komentar.");
        }
        return ret;
    }

    private UkolControl parseToUkolObject(HashMap<String, String> hm) throws NeprevoditelnyObjekt, DatabazovaChyba {
        UkolControl ret = null;
        if (hm.containsKey("task_id") && hm.containsKey("project_id") && hm.containsKey("name") && hm.containsKey("description") && hm.containsKey("date_start") && hm.containsKey("date_end") && hm.containsKey("date_deadline") && hm.containsKey("user_id") && hm.containsKey("status") && hm.containsKey("time_spent")) {
            ProjektControl projekt = parseToProjectObject(this.provedSelect("SELECT * FROM projects WHERE project_id='" + hm.get("project_id") + "'").get(0));
            Uzivatel author = null;
            if (!hm.get("user_id").equals("")) {
                ArrayList<HashMap<String, String>> uzivatelSelect = this.provedSelect("SELECT * FROM users WHERE user_id='" + hm.get("user_id") + "'");
                /*
                 * Kontrola pro pripad, ze uzivatel je prirazen, ale uz byl vymazan z databaze, tj nejde dohledat
                 */
                if (!uzivatelSelect.isEmpty()) {
                    author = parseToUzivatelObject(uzivatelSelect.get(0));
                }
            }
            ArrayList<Komentar> komentare = new ArrayList<Komentar>();
            ret = new UkolControl(Integer.parseInt(hm.get("task_id")), hm.get("name"), hm.get("description"), sqlDateToCalendar(hm.get("date_start")), sqlDateToCalendar(hm.get("date_end")),
                    sqlDateToCalendar(hm.get("date_deadline")), projekt, author, stringToUkolStatus(hm.get("status")), komentare,
                    (hm.get("time_spent").isEmpty()) ? 0 : Integer.parseInt(hm.get("time_spent")));

            ArrayList<HashMap<String, String>> tmpKomentare = this.provedSelect("SELECT * FROM comments WHERE task_id='" + hm.get("task_id") + "'");
            for (HashMap<String, String> hmKomentar : tmpKomentare) {
                ret.getModel().getKomentare().add(parseToKomentarObject(hmKomentar, ret));
            }
        } else {
            throw new NeprevoditelnyObjekt("Objekt nelze prevest na objekt typu Ukol.");
        }
        return ret;
    }

    private ProjektControl parseToProjectObject(HashMap<String, String> hm) throws NeprevoditelnyObjekt, DatabazovaChyba {
        ProjektControl ret = null;
        if (hm.containsKey("project_id") && hm.containsKey("name") && hm.containsKey("description") && hm.containsKey("date_start") && hm.containsKey("date_end") && hm.containsKey("date_deadline") && hm.containsKey("time_spent") && hm.containsKey("user_id") && hm.containsKey("status")) {
            ArrayList<HashMap<String, String>> uzivatelSelect = provedSelect("SELECT * FROM users WHERE user_id='" + hm.get("user_id") + "'");
            /*
             * Kontrola pro pripad, ze uzivatel je prirazen, ale uz byl vymazan z databaze, tj nejde dohledat
             */
            if (!uzivatelSelect.isEmpty()) {
                Uzivatel odpovednyUzivatel = parseToUzivatelObject(uzivatelSelect.get(0));
                ret = new ProjektControl(Integer.parseInt(hm.get("project_id")), hm.get("name"), hm.get("description"), sqlDateToCalendar(hm.get("date_start")),
                        sqlDateToCalendar(hm.get("date_end")), sqlDateToCalendar(hm.get("date_deadline")), Integer.parseInt(hm.get("time_spent")), odpovednyUzivatel, stringToUkolStatus(hm.get("status")));
            } else {
                ret = new ProjektControl(Integer.parseInt(hm.get("project_id")), hm.get("name"), hm.get("description"), sqlDateToCalendar(hm.get("date_start")),
                        sqlDateToCalendar(hm.get("date_end")), sqlDateToCalendar(hm.get("date_deadline")), Integer.parseInt(hm.get("time_spent")), null, stringToUkolStatus(hm.get("status")));
            }
        } else {
            throw new NeprevoditelnyObjekt("Objekt nelze prevest na objek typu Projekt.");
        }
        return ret;
    }

    public boolean userNameExists(String username) throws DatabazovaChyba {
        if (this.provedSelect("SELECT email FROM users WHERE email='" + username + "'").isEmpty()) {
            return false;
        }
        return true;
    }

    public boolean passwordMatch(String username, String heslo) throws DatabazovaChyba {
        if (!userNameExists(username)) {
            return false;
        }
        HashMap<String, String> hm = this.provedSelect("SELECT password FROM users WHERE email='" + username + "'").get(0);
        String md5Password = hm.get("password");

        if (!md5Password.equals(vratMd5(heslo))) {
            return false;
        }
        return true;
    }

    public boolean projectExists(ProjektControl project) throws DatabazovaChyba {
        if (this.provedSelect("SELECT project_id FROM projects WHERE project_id='" + project.getModel().getID() + "'").isEmpty()) {
            return false;
        }
        return true;
    }

    public boolean ukolExists(UkolControl ukol) throws DatabazovaChyba {
        if (this.provedSelect("SELECT task_id FROM tasks WHERE task_id='" + ukol.getModel().getID() + "'").isEmpty()) {
            return false;
        }
        return true;
    }

    /** Prevede objekt typu GregorianCalendar na String, ktery se da ulozit do databaze.
     * 
     * @param calendar zdrojovy kalendar
     * @param time priznak, zda se bude prevadet i cas. Kdzy je false, prevede se jenom datum.
     * @return retezec s validnim datem pro ulozeni do databaze.
     */
    private String calendarToSQLDate(GregorianCalendar calendar, boolean time) {
        if (calendar == null) {
            return null;
        }
        if (calendar.get(GregorianCalendar.YEAR) == 0) {
            return null;
        }
        String year = String.valueOf(calendar.get(GregorianCalendar.YEAR));
        String month = String.valueOf(calendar.get(GregorianCalendar.MONTH) + 1);
        String day = String.valueOf(calendar.get(GregorianCalendar.DAY_OF_MONTH));
        String hour = String.valueOf(calendar.get(GregorianCalendar.HOUR_OF_DAY));
        String minute = String.valueOf(calendar.get(GregorianCalendar.MINUTE));
        String second = String.valueOf(calendar.get(GregorianCalendar.SECOND));
        if (time) {
            return year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
        } else {
            return year + "-" + month + "-" + day;
        }
    }

    private GregorianCalendar sqlDateToCalendar(String sqlDate) {
        if (sqlDate.isEmpty()) {
            return null;
        }
        GregorianCalendar ret = (GregorianCalendar) GregorianCalendar.getInstance();
        String[] parts = sqlDate.split(" ");
        String[] datePart = parts[0].split("-");
        String[] timePart = null;
        if (parts.length == 2) {
            timePart = parts[1].split(":");
        }

        int year = Integer.valueOf(datePart[0]);
        if (year == 0) {
            return null;
        }
        int month = Integer.valueOf(datePart[1]) - 1;
        int day = Integer.valueOf(datePart[2]);
        int hour = timePart != null ? Integer.valueOf(timePart[0]) : 0;
        int minute = timePart != null ? Integer.valueOf(timePart[1]) : 0;
        int second = timePart != null ? Integer.valueOf(timePart[2]) : 0;
        ret.set(GregorianCalendar.YEAR, year);
        ret.set(GregorianCalendar.MONTH, month);
        ret.set(GregorianCalendar.DAY_OF_MONTH, day);
        ret.set(GregorianCalendar.HOUR_OF_DAY, hour);
        ret.set(GregorianCalendar.MINUTE, minute);
        ret.set(GregorianCalendar.SECOND, second);
        return ret;
    }

    private UkolStatus stringToUkolStatus(String str) {
        if (str.equals("NEW") || str.equals("Nový")) {
            return UkolStatus.NEW;
        } else if (str.equals("ACCEPTED") || str.equals("Přijmutý")) {
            return UkolStatus.ACCEPTED;
        } else if (str.equals("SUSPENDED") || str.equals("Pozastavený")) {
            return UkolStatus.SUSPENDED;
        } else if (str.equals("COMPLETE") || str.equals("Hotový")) {
            return UkolStatus.COMPLETE;
        } else if (str.equals("CONFIRMED") || str.equals("Potvrzený")) {
            return UkolStatus.CONFIRMED;
        } else if (str.equals("ABANDONED") || str.equals("Opuštěný")) {
            return UkolStatus.ABANDONED;
        } else {
            return null;
        }
    }

    private String ukolStatusToString(UkolStatus ukolStatus) {
        return ukolStatus.toString();
    }
}
